package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.tianji.learning.task.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author xiaohai
 * @since 2024-09-12
 */
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final ILearningLessonService lessonService;

    private final CourseClient courseClient;

    private final LearningRecordDelayTaskHandler taskHandler;

    /**
     * 根据课程id查询学习记录
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonDTO queryByCourseId(Long courseId) {
        Long userId = UserContext.getUser();
        // 1.根据courseId和userId查询出课表id
        LearningLesson lesson = lessonService.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        if (lesson == null) {
            return null;
        }
        // 2.根据课表id查询出学习记录列表
        List<LearningRecord> recordList = lambdaQuery()
                .eq(LearningRecord::getLessonId, lesson.getId())
                .list();
        List<LearningRecordDTO> records = BeanUtils.copyList(recordList, LearningRecordDTO.class);
        // 3.封装到LearningLessonDTO里面
        LearningLessonDTO dto = new LearningLessonDTO();
        dto.setId(lesson.getId());
        dto.setLatestSectionId(lesson.getLatestSectionId());
        dto.setRecords(records);
        return dto;
    }

    /**
     * 提交学习记录
     *
     * @param dto
     */
    @Override
    public void addLearningRecord(LearningRecordFormDTO dto) {
        // 1.获得用户id
        Long userId = UserContext.getUser();
        // 2.处理学习记录
        // 2.1 判断提交的类型
        boolean finished = false;
        if (dto.getSectionType() == SectionType.EXAM) {
            // 2.2 处理考试
            finished = handleExam(userId, dto);
        } else {
            // 2.3 处理视频
            finished = handleVideo(userId, dto);
        }
        // 已经学完的小节数、状态、最近学习的小节id、最近学习的时间
        // 如果没有小节学完,课表是不需要更新的; 不是第一次学完,我们只需要把最后一次提交的数据更新就可以了[在延迟任务里面]
        if (!finished) {
            return;
        }

        // 3.处理课表
        handleLesson(dto);
    }

    // 处理课表
    private void handleLesson(LearningRecordFormDTO dto) {
        // 1.根据课表id查询课表
        LearningLesson lesson = lessonService.getById(dto.getLessonId());
        if (lesson == null) {
            throw new DbException("课表数据不存在");
        }
        // 2.判断是否全部学完(查询出当前课程的小节总数,已经学习的小节数+1 >= 当前课程的小节总数)
        // 2.1 有小节学完,才有必要去判断是否全部学完
        boolean allLearned = false;
        //if (finished) {
        CourseFullInfoDTO course = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (course != null) {
            Integer sectionNum = course.getSectionNum();
            allLearned = lesson.getLearnedSections() + 1 >= sectionNum;
            // }
        }
        // 3.更新课表(课表学习小节数,课表状态,课表最近学习小节,课程最新学习时间)
        boolean success = lessonService.lambdaUpdate()
                .set(LearningLesson::getLearnedSections, lesson.getLearnedSections() + 1)
                .set(LearningLesson::getLatestLearnTime, LocalDateTime.now())
                .set(LearningLesson::getLatestSectionId, dto.getSectionId())
                //.set(allLearned,LearningLesson::getStatus, LessonStatus.FINISHED)
                // 如果这门课是第一次学习,我们需要更新这个状态是学习中
                .set(lesson.getStatus() == LessonStatus.NOT_BEGIN, LearningLesson::getStatus, LessonStatus.LEARNING)
                .eq(LearningLesson::getId, lesson.getId())
                .update();
        if (!success) {
            throw new DbException("课表数据更新失败");
        }
    }

    // 处理视频
    private boolean handleVideo(Long userId, LearningRecordFormDTO dto) {
        // 1.根据课表id和小节id查询学习记录
        /*LearningRecord old = lambdaQuery()
                .eq(LearningRecord::getLessonId, dto.getLessonId())
                .eq(LearningRecord::getSectionId, dto.getSectionId())
                .one();*/
        LearningRecord old = queryLearningRecord(dto);
        // 2.不存在,新增学习记录
        if (old == null) {
            LearningRecord record = BeanUtils.copyBean(dto, LearningRecord.class);
            record.setUserId(userId);
            boolean success = save(record);
            if (!success) {
                throw new DbException("保存学习视频记录失败");
            }
            return false;
        }
        // 3.判断是否是第一次学完
        boolean finished = !old.getFinished() && dto.getMoment() * 2 > dto.getDuration();
        if (!finished) {
            // 不是第一次学完,缓存到Redis里面,提交延迟任务 old是数据库里面之前的记录,新提交的进度在dto里面
            LearningRecord record = new LearningRecord();
            record.setLessonId(dto.getLessonId());
            record.setSectionId(dto.getSectionId());
            record.setMoment(dto.getMoment());
            record.setId(old.getId());
            record.setFinished(old.getFinished());
            taskHandler.addLearningRecordTask(record);
            return false;
        }

        // 4.更新学习记录
        boolean success = lambdaUpdate()
                .set(LearningRecord::getMoment, old.getMoment())
                .set(LearningRecord::getFinished, true)
                .set(LearningRecord::getFinishTime, LocalDate.now())
                .eq(LearningRecord::getId, old.getId())
                .update();
        if (!success) {
            throw new DbException("更新学习视频进度失败");
        }
        // 5.删除Redis里面的缓存
        taskHandler.cleanRecordCache(dto.getLessonId(), dto.getSectionId());

        return finished;
    }

    // 查询当前课程的小节的学习记录
    private LearningRecord queryLearningRecord(LearningRecordFormDTO dto) {
        // 1.先从Redis里面查询
        LearningRecord record = taskHandler.readRecordCache(dto.getLessonId(), dto.getSectionId());
        // 2.如果没有，从MySQL里面查询
        if (record == null) {
            record = lambdaQuery()
                    .eq(LearningRecord::getLessonId, dto.getLessonId())
                    .eq(LearningRecord::getSectionId, dto.getSectionId())
                    .one();
            // 3.如果MySQL里面存在数据，再存到Redis里面
            if (record != null) {
                // 存到Redis里面
                taskHandler.writeRecordCache(record);
            }
        }

        return record;
    }

    // 处理考试
    private boolean handleExam(Long userId, LearningRecordFormDTO dto) {
        // 1.创建record对象
        LearningRecord record = BeanUtils.copyBean(dto, LearningRecord.class);
        // 2.设置属性
        record.setUserId(userId);
        record.setFinished(true);
        record.setFinishTime(dto.getCommitTime());
        // 3.保存
        boolean success = save(record);
        if (!success) {
            throw new DbException("保存考试记录失败");
        }
        return true;
    }
}
